﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Chefbook.ViewModels;
using Chefbook.DataAccess;
using System.Collections.Specialized;
using UnitTests.Properties;

namespace UnitTests.ViewModelsTests
{
    public class RecipeListViewModelFixture
    {
        private CategoryViewModel category;
        private RecipeListViewModel recipeListVM;
        private ChefbookXmlRepository repo;
        private RecipeViewModel testRecipe;
        private List<RecipeViewModel> allRecipes;


        [SetUp]
        public void Init()
        {
            repo = new ChefbookXmlRepository(Resources.RecipeXmlPath, Resources.CategoryXmlPath);
            category = ViewModelTestHelper.NewCategoryViewModel(null, repo);
            category.Name = "Foo";
            allRecipes = repo.Recipes.Values.ToList();
            testRecipe = allRecipes[2];
            recipeListVM = new RecipeListViewModel(category, repo);
        }

        [Test]
        public void DefaultSelectedRecipeBoundsTest()
        {
            Assert.AreEqual(null, recipeListVM.SelectedRecipe); //Category has no recipes
            var recipe1 = ViewModelTestHelper.NewRecipeViewModel(category, repo); recipe1.Name = "Recipe1";
            Assert.AreEqual(recipe1, recipeListVM.SelectedRecipe);
            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(category, repo); recipe2.Name = "Recipe2";
            recipeListVM.SelectedRecipe = recipe2;
            var recipe3 = ViewModelTestHelper.NewRecipeViewModel(category, repo); recipe3.Name = "Recipe3";
            category.RemoveRecipe(recipe2);
            Assert.AreEqual(recipe3, recipeListVM.SelectedRecipe); //Assert recipe after removed one is selected
            category.AddRecipe(recipe2); //Added to end of list
            recipeListVM.SelectedRecipe = recipe2;
            category.RemoveRecipe(recipe2);
            Assert.AreEqual(recipe3, recipeListVM.SelectedRecipe); //If last recipe in the list, assert that it select the one before it
        }

        [Test]
        public void SelectedRecipePropertyChangedTest()
        {
            recipeListVM.AssertPropertyChangedFired(() =>
                recipeListVM.SelectedRecipe = testRecipe, "SelectedRecipe");
            Assert.AreEqual(testRecipe, recipeListVM.SelectedRecipe);
            recipeListVM.AssertPropertyChangedFired(() =>
                recipeListVM.SelectedRecipe = testRecipe);
            Assert.AreEqual(testRecipe, recipeListVM.SelectedRecipe);
        }

        [Test]
        public void ChangeCategoryTest()
        {
            var recipe1 = ViewModelTestHelper.NewRecipeViewModel(category, repo);
            recipe1.Name = "MyRecipe1";
            var category2 = ViewModelTestHelper.NewCategoryViewModel(null, repo);
            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(category2, repo);
            recipe1.Name = "MyRecipe2";
            Assert.Contains(recipe1, recipeListVM.Recipes);
            Assert.False(recipeListVM.Recipes.Contains(recipe2));
            recipeListVM.AssertPropertyChangedFired(() => recipeListVM.CurrentCategory = category2, "CurrentCategory", "SelectedRecipe");
            Assert.AreEqual(recipe2, recipeListVM.SelectedRecipe); 
            Assert.Contains(recipe2, recipeListVM.Recipes);
            Assert.False(recipeListVM.Recipes.Contains(recipe1));
            
        }

        [Test]
        public void CreateRecipeListViewModelForCategoryWithTwoRecipes()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            var cat2 = ViewModelTestHelper.NewCategoryViewModel(null, repo);
            cat2.AddRecipe(testRecipe);

            var recipeListVM2 = new RecipeListViewModel(cat2, repo);

            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            cat2.AddRecipe(recipe2);

            
            Assert.AreEqual(2, recipeListVM2.Recipes.Count);
            Assert.Contains(testRecipe, recipeListVM2.Recipes);
            Assert.Contains(recipe2, recipeListVM2.Recipes);
        }

        [Test]
        public void AddARecipeThenChangeCategoriesAndAddAnother()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            testRecipe.Name = "Recipe";
            var cat2 = ViewModelTestHelper.NewCategoryViewModel(null, repo);
            cat2.AddRecipe(testRecipe);

            recipeListVM = new RecipeListViewModel(cat2,repo);
            Assert.AreEqual(1, recipeListVM.Recipes.Count);
            Assert.Contains(testRecipe, recipeListVM.Recipes);

            var cat3 = ViewModelTestHelper.NewCategoryViewModel(null, repo);
            recipeListVM.CurrentCategory = cat3;

            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            recipe2.Name = "Recipe2";
            cat3.AddRecipe(recipe2);

            Assert.AreEqual(1, recipeListVM.Recipes.Count);
            Assert.False(recipeListVM.Recipes.Contains(testRecipe));
            Assert.Contains(recipe2, recipeListVM.Recipes);
        }

        [Test]
        public void CreateRecipeListViewModelForCategoryWithASubWithTwoRecipes()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);

            var cat = ViewModelTestHelper.NewCategoryViewModel(null, repo);

            var subCategory = ViewModelTestHelper.NewCategoryViewModel(cat, repo);
            subCategory.AddRecipe(testRecipe);
            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            subCategory.AddRecipe(recipe2);

            var recipeListVM = new RecipeListViewModel(cat, repo);
            Assert.AreEqual(2, recipeListVM.Recipes.Count);
            Assert.Contains(testRecipe, recipeListVM.Recipes);
            Assert.Contains(recipe2, recipeListVM.Recipes);
        }

        [Test]
        public void ChangeToCategoryWithASubWithTwoRecipes()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);

            var cat = ViewModelTestHelper.NewCategoryViewModel(null, repo);

            var subCategory = ViewModelTestHelper.NewCategoryViewModel(cat, repo);
            subCategory.AddRecipe(testRecipe);
            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            subCategory.AddRecipe(recipe2);

            recipeListVM.CurrentCategory =  cat;

            Assert.AreEqual(2, recipeListVM.Recipes.Count);
            Assert.Contains(testRecipe, recipeListVM.Recipes);
            Assert.Contains(recipe2, recipeListVM.Recipes);
        }

        [Test]
        public void ChangeToCategoryThenAddASubWithTwoRecipes()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);

            var cat = ViewModelTestHelper.NewCategoryViewModel(null, repo);
            recipeListVM.CurrentCategory = cat;

            var subCategory = ViewModelTestHelper.NewCategoryViewModel(cat, repo);
            subCategory.AddRecipe(testRecipe);
            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            subCategory.AddRecipe(recipe2);

            Assert.AreEqual(2, recipeListVM.Recipes.Count);
            Assert.Contains(testRecipe, recipeListVM.Recipes);
            Assert.Contains(recipe2, recipeListVM.Recipes);
        }

        [Test]
        public void CreateRecipeListViewModelForCategoryWithTwoRecipesAndRemoveThem()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            var cat2 = ViewModelTestHelper.NewCategoryViewModel(null, repo);
            cat2.AddRecipe(testRecipe);
            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            cat2.AddRecipe(recipe2);
            recipeListVM = new RecipeListViewModel(cat2, repo);

            cat2.RemoveRecipe(testRecipe);

            Assert.AreEqual(1, recipeListVM.Recipes.Count);
            Assert.False(recipeListVM.Recipes.Contains(testRecipe));
            Assert.Contains(recipe2, recipeListVM.Recipes);

            cat2.RemoveRecipe(recipe2);

            Assert.AreEqual(0, recipeListVM.Recipes.Count);
            Assert.False(recipeListVM.Recipes.Contains(testRecipe));
            Assert.False(recipeListVM.Recipes.Contains(recipe2));
        }

        [Test]
        public void CreateRecipeListViewModelForCategoryWithASubWithTwoRecipesAndRemoveThem()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);

            var parentCategory = ViewModelTestHelper.NewCategoryViewModel(null, repo);

            var subCategory = ViewModelTestHelper.NewCategoryViewModel(parentCategory, repo);
            subCategory.AddRecipe(testRecipe);

            var recipe2 = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            subCategory.AddRecipe(recipe2);

            recipeListVM = new RecipeListViewModel(parentCategory,repo);

            subCategory.RemoveRecipe(testRecipe);

            Assert.AreEqual(1, recipeListVM.Recipes.Count);
            Assert.False(recipeListVM.Recipes.Contains(testRecipe));
            Assert.Contains(recipe2, recipeListVM.Recipes);

            subCategory.RemoveRecipe(recipe2);

            Assert.AreEqual(0, recipeListVM.Recipes.Count);
            Assert.False(recipeListVM.Recipes.Contains(testRecipe));
            Assert.False(recipeListVM.Recipes.Contains(recipe2));
        }

        [Test]
        public void AddRecipeFiresCollectionChangedOnRecipesOfParent()
        {
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            testRecipe.Name = "I am Recipe";
            var args = recipeListVM.Recipes.AssertCollectionChangedFiredAndReturnArgs(
                () =>category.AddRecipe(testRecipe));
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(testRecipe, args.NewItems);
            Assert.Contains(testRecipe, recipeListVM.Recipes);
            Assert.Contains(category, testRecipe.Categories);
        }

        [Test]
        public void AddRecipeToSubFiresCollectionChangedOnRecipesOfParent()
        {
            var sub = ViewModelTestHelper.NewCategoryViewModel(category, repo);
            var subRecipeListVM = new RecipeListViewModel(sub,repo);
            testRecipe = ViewModelTestHelper.NewRecipeViewModel(null, repo);
            var args = recipeListVM.Recipes.AssertCollectionChangedFiredAndReturnArgs(
                () => sub.AddRecipe(testRecipe));
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(testRecipe, args.NewItems);
            Assert.Contains(testRecipe, subRecipeListVM.Recipes);
        }

    }
}
